﻿using System;
using System.ComponentModel;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Text;
using System.Windows.Input;

namespace MouseExtender.Logic.Helpers
{
    /// <summary>
    /// This class holds all Win32 operations (Pinvoke, Constants, etc)
    /// </summary>
    public static class Win32Helper
    {
        #region Structures
        [StructLayout(LayoutKind.Sequential)]
        public struct SHFILEINFO
        {
            public IntPtr hIcon;
            public IntPtr iIcon;
            public uint dwAttributes;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 260)]
            public string szDisplayName;
            [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 80)]
            public string szTypeName;
        }
        #endregion

        #region DllImports

        [DllImport("user32.dll")]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool SetForegroundWindow(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern IntPtr GetForegroundWindow();

        [DllImport("user32.dll")]
        public static extern uint GetWindowThreadProcessId(IntPtr hWnd, IntPtr processId);

        [DllImport("user32.dll", EntryPoint = "GetWindowThreadProcessId")]
        public static extern void GetWindowProcessId(IntPtr hWnd, out int processId);

        [DllImport("user32.dll")]
        public static extern bool AttachThreadInput(uint idAttach, uint idAttachTo, bool fAttach);

        [DllImport("shell32.dll", CharSet = CharSet.Ansi)]
        public static extern IntPtr SHGetFileInfo(string pszPath, uint dwFileAttributes, ref SHFILEINFO psfi, uint cbSizeFileInfo, uint uFlag);

        [DllImport("user32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        public static extern bool DestroyIcon(IntPtr hIcon);

        [DllImport("gdi32.dll")]
        public static extern bool DeleteObject(IntPtr hObject);

        [DllImport("shell32.DLL", EntryPoint = "ExtractIconEx")]
        public static extern int ExtractIconExA(string lpszFile, int nIconIndex, ref int phiconLarge, ref int phiconSmall, int nIcon);

        [DllImport("shell32.DLL", EntryPoint = "ExtractAssociatedIcon")]
        public static extern IntPtr ExtractAssociatedIconA(IntPtr hInst, string lpIconPath, ref int lpiIcon);

        [DllImport("wtsapi32.dll")]
        public static extern bool WTSRegisterSessionNotification(IntPtr hWnd, int dwFlags);

        [DllImport("wtsapi32.dll")]
        public static extern bool WTSUnRegisterSessionNotification(IntPtr hWnd);

        [DllImport("user32.dll")]
        public static extern IntPtr GetWindowLong(IntPtr hWnd, int nIndex);

        [DllImport("user32.dll", EntryPoint = "SetWindowLongPtr", SetLastError = true)]
        public static extern IntPtr IntSetWindowLongPtr(IntPtr hWnd, int nIndex, IntPtr dwNewLong);

        [DllImport("user32.dll", EntryPoint = "SetWindowLong", SetLastError = true)]
        public static extern Int32 IntSetWindowLong(IntPtr hWnd, int nIndex, Int32 dwNewLong);

        [DllImport("kernel32.dll", EntryPoint = "SetLastError")]
        public static extern void SetLastError(int dwErrorCode);

        [DllImport("shlwapi.dll", CharSet = CharSet.Auto, SetLastError = false)]
        private static extern bool PathFindOnPath([MarshalAs(UnmanagedType.LPTStr)] StringBuilder pszFile, IntPtr unused);

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true, ExactSpelling = true)]
        private static extern short GetKeyState(int virtualKeyCode);

        #endregion

        #region Constants

        public const uint SHGFI_ICON = 0x100;
        public const uint SHGFI_LARGEICON = 0x0;
        public const uint SHGFI_SMALLICON = 0x1;
        //see http://www.codeguru.com/cpp/com-tech/shell/article.php/c4511
        public const uint SHGFI_USEFILEATTRIBUTES = 0x000000010;
        public const uint SHGFI_TYPENAME = 0x000000400;
        public const uint FILE_ATTRIBUTE_DIRECTORY = 0x10;
        public const uint FILE_ATTRIBUTE_NORMAL = 0x80;

        public const uint TEST = 0x2;

        private const int VK_SHIFT = 0x10;
        private const int VK_CONTROL = 0x11;
        private const int VK_ALT = 0x12;
        private const int VK_LWIN = 0x5B;
        private const int VK_RWIN = 0x5C;

        #endregion

        public static string GetActiveProcessExecutableName(out bool isProcessName)
        {
            isProcessName = false;
            String processName = String.Empty;
            Process process = GetActiveProcess();
            if (process != null)
            {
                try
                {
                    processName = process.MainModule.FileName;
                }
                catch (Win32Exception ex)
                {
                    isProcessName = true;
                    if (ex.NativeErrorCode == 5)
                        processName = process.ProcessName;
                    else
                        throw;
                }
            }

            return processName;
        }

        public static Process GetActiveProcess()
        {
            Process process = null;
            IntPtr windowPointer = GetForegroundWindow();
            int processId = 0;
            GetWindowProcessId(windowPointer, out processId);
            if (processId != 0)
                process = Process.GetProcessById(processId);
            return process;
        }

        //TODO: try using PathSearchAndQualify
        public static string FindInPath(String shortFileName)
        {
            const int MAX_PATH = 260;
            StringBuilder path = new StringBuilder(shortFileName, MAX_PATH);
            bool found = PathFindOnPath(path, IntPtr.Zero);
            return found ? path.ToString() : shortFileName;
        }

        public static ModifierKeys GetModifierKeys()
        {
            ModifierKeys modifiers = ModifierKeys.None;
            bool isShift = GetKeyState(VK_SHIFT) < 0;
            bool isCtrl = GetKeyState(VK_CONTROL) < 0;
            bool isAlt = GetKeyState(VK_ALT) < 0;
            bool isWin = GetKeyState(VK_LWIN) < 0 || GetKeyState(VK_RWIN) < 0;

            if (isShift)
                modifiers |= ModifierKeys.Shift;
            if (isCtrl)
                modifiers |= ModifierKeys.Control;
            if (isAlt)
                modifiers |= ModifierKeys.Alt;
            //if (isWin) //maybe in future
            //  modifiers |= ModifierKeys.Windows;

            return modifiers;
        }
    }
}
